package com.hex.ds.hdtp.core.app.data.contrast.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.hex.ds.hdtp.core.app.common.page.PageData;
import com.hex.ds.hdtp.core.app.common.page.converter.IPageAndPageDtoConverter;
import com.hex.ds.hdtp.core.app.common.page.dto.PageDto;
import com.hex.ds.hdtp.core.app.common.util.ExcelDownUtil;
import com.hex.ds.hdtp.core.app.data.contrast.converter.ICrossDbContrastListConverter;
import com.hex.ds.hdtp.core.app.data.contrast.dto.ContrastListExcelDto;
import com.hex.ds.hdtp.core.app.data.contrast.dto.ContrastResultExcelDto;
import com.hex.ds.hdtp.core.app.data.contrast.dto.request.CrossDbContrastListRequestDto;
import com.hex.ds.hdtp.core.app.data.contrast.dto.request.CrossDbContrastTaskRequestDto;
import com.hex.ds.hdtp.core.app.data.contrast.dto.response.CrossDbContrastListResponseDto;
import com.hex.ds.hdtp.core.app.data.contrast.dto.response.CrossDbContrastResultResponseDto;
import com.hex.ds.hdtp.core.app.data.contrast.service.ICrossDbContrastListService;
import com.hex.ds.hdtp.core.app.data.contrast.service.ICrossDbContrastResultService;
import com.hex.ds.hdtp.core.app.metadata.service.ISourceConfInfoService;
import com.hex.ds.hdtp.core.app.metadata.service.ISourceInfoService;
import com.hex.ds.hdtp.core.inf.common.page.Page;
import com.hex.ds.hdtp.core.inf.data.contrast.po.CrossDbContrastListPo;
import com.hex.ds.hdtp.core.inf.data.contrast.po.CrossDbContrastTaskPo;
import com.hex.ds.hdtp.core.inf.data.contrast.repository.ICrossDbContrastListRepository;
import com.hex.ds.hdtp.core.inf.data.contrast.repository.impl.CrossDbContrastTaskRepository;
import com.hex.ds.hdtp.core.inf.data.contrast.service.IDataInfoService;
import com.hex.ds.hdtp.core.inf.data.contrast.service.impl.DataInfoSelectHandlerContext;
import com.hex.ds.hdtp.core.inf.metadata.entity.TableFieldsEntity;
import com.hex.ds.hdtp.core.inf.metadata.po.SourceInfoPo;
import liquibase.util.file.FilenameUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.hex.bigdata.hbda.utils.FileUtil.upload;

/**
 * @Package com.hex.ds.hdtp.core.app.data.contrast.service.impl
 * @ClassName CrossDbContrastListService
 * @Description 跨库比对清单-业务接口实现
 * @Author Wang zhihao
 * @Date 2023/8/6 22:07
 * @Version v1.0
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CrossDbContrastListService implements ICrossDbContrastListService {

    private final ICrossDbContrastListRepository crossDbContrastListRepository;

    private final ICrossDbContrastListConverter converter;

    private final IPageAndPageDtoConverter pageAndPageDtoConverter;

    private final ICrossDbContrastResultService crossDbContrastResultService;

    private final ISourceInfoService sourceInfoService;

    private final DataInfoSelectHandlerContext dataInfoSelectHandlerContext;

    private final ISourceConfInfoService sourceConfInfoService;

    private final CrossDbContrastTaskRepository crossDbContrastTaskRepository;


    @Value("${file.path}")
    private String filePath;

    private IDataInfoService getIDataInfoService(String type) {
        return dataInfoSelectHandlerContext.getDataInfoService(type);
    }

    /*
     * @Method: queryListByPage <br>
     * @Param: [crossDbContrastListRequestDto, pageDto] <br>
     * @Return: com.hex.ds.hdtp.core.app.common.page.PageData <br>
     * @Description：分页查询<br>
     * @Author： wz.li<br>
     * @Date： 2023/7/24 19:42 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public PageData queryListByPage(CrossDbContrastListRequestDto crossDbContrastListRequestDto, PageDto pageDto) {
        final Page<CrossDbContrastListPo> pageResult = crossDbContrastListRepository.queryListByPage(
                converter.toCrossDbContrastListPo(crossDbContrastListRequestDto), pageAndPageDtoConverter.toPage(pageDto));
        List<CrossDbContrastListResponseDto> responseDtos = converter.toCrossDbContrastListResponseDtoList(pageResult.getData());
        for (CrossDbContrastListResponseDto crossDbContrastList : responseDtos) {
            List<TableFieldsEntity> tablesStructure = sourceInfoService.tablesStructure(crossDbContrastList.getServerPkId(), crossDbContrastList.getSourceDbName(), crossDbContrastList.getSourceTableName());
            crossDbContrastList.setTableFieldsList(tablesStructure.stream().map(TableFieldsEntity::getColumnName).collect(Collectors.toList()));
        }
        return new PageData(responseDtos, pageResult.getTotal());
    }

    /*
     * @Method: add <br>
     * @Param: [crossDbContrastListRequestDto] <br>
     * @Return: void <br>
     * @Description：新增跨库比对清单<br>
     * @Author： wz.li<br>
     * @Date： 2023/7/24 19:42 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void add(CrossDbContrastListRequestDto crossDbContrastListRequestDto) {
        int count = crossDbContrastListRepository.add(converter.toCrossDbContrastListPo(crossDbContrastListRequestDto));
        if (count != 1) {
            throw new RuntimeException("新增跨库对比清单失败");
        }
    }

    /**
     * @param crossDbContrastListRequestDto
     * @Method addWithBackId
     * @Param
     * @Return java.lang.String
     * @Description 新增并返回主键
     * @Author Wang zhihao
     * @Date 2023/8/16 17:43
     * @Version V1.0
     */
    @Override
    public String addWithBackId(CrossDbContrastListRequestDto crossDbContrastListRequestDto) {
        return crossDbContrastListRepository.addWithBackId(converter.toCrossDbContrastListPo(crossDbContrastListRequestDto));
    }

    /**
     * @Method exportExcel
     * @Param
     * @Param crossDbContrastListRequestDto
     * @Param pageDto
     * @Return java.lang.String
     * @Description 日志导出
     * @Author xin.zhao
     * @Date 2023/11/8 11:17
     * @Version V1.0
     */
    @Override
    public String exportExcel(CrossDbContrastListRequestDto crossDbContrastListRequestDto, PageDto pageDto) {
        PageData pageData = queryListResultByPage(crossDbContrastListRequestDto, pageDto);
        List<CrossDbContrastListResponseDto> contrastResult = (List<CrossDbContrastListResponseDto>) pageData.getContent();
        ArrayList<ContrastResultExcelDto> contrastResultExcelDtos = new ArrayList<>();
        int index = 1;
        for (CrossDbContrastListResponseDto crossDbContrastListResponseDto : contrastResult) {
            ContrastResultExcelDto contrastResultExcelDto = converter.toContrastExcelDto(crossDbContrastListResponseDto, index);
            contrastResultExcelDtos.add(contrastResultExcelDto);
            index++;
        }
        String backUpPath = createSavePath();
        String templateFilePath = "static/contrastResultTemplate.xlsx";
        String saveFilePath = backUpPath + File.separator + "跨库比对任务结果.xlsx";
        ExcelDownUtil.generatedExcel(contrastResultExcelDtos, templateFilePath, saveFilePath);
        return saveFilePath;
    }

    /**
     * @param pkIds:
     * @return ByteArrayResource
     * @author Liang Fuhao
     * @description 导出比对清单
     * @date 2023/12/22 16:29
     */
    @Override
    public ByteArrayResource exportContrastList(List<String> pkIds) {
        String backUpPath = createSavePath();
        String templateFilePath = "static/ContrastListTemplate.xlsx";
        List<ContrastListExcelDto> contrastListExcelDtoList = pkIds.stream()
                .map(crossDbContrastListRepository::queryById)
                .map(tableTransferListResponseDto -> {
                            ContrastListExcelDto contrastListExcelDto = new ContrastListExcelDto();
                            BeanUtils.copyProperties(tableTransferListResponseDto, contrastListExcelDto);
                            SourceInfoPo sourceInfoPo = sourceInfoService.queryByPkId(contrastListExcelDto.getContrastServerPkId());
                            contrastListExcelDto.setContrastSourceInfoCode(sourceInfoPo.getSourceCode());
                            return contrastListExcelDto;
                        }
                ).collect(Collectors.toList());
        String saveFilePath = backUpPath + File.separator + "比对清单模板.xlsx";
        ExcelDownUtil.generatedExcel(contrastListExcelDtoList, templateFilePath, saveFilePath);
        byte[] bytes;
        try {
            bytes = Files.readAllBytes(new File(saveFilePath).toPath());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return new ByteArrayResource(bytes);
    }

    @Override
    public Map<String, Object> importContrastList(MultipartFile file, CrossDbContrastTaskRequestDto contrastTask) {
        Map<String, Object> resultMap = new HashMap<>();
        try {
            String backUpFilePath = backupUploadFile(file);
            List<ContrastListExcelDto> contrastListExcelDtos = getExcelData(backUpFilePath);
            String serverPkId = contrastTask.getServerPkId();
            contrastListExcelDtos
                    .forEach(dto -> {
                        dto.setServerPkId(serverPkId);
                        SourceInfoPo sourceInfoPo = sourceInfoService.queryBySourceCode(dto.getContrastSourceInfoCode());
                        dto.setContrastServerPkId(sourceInfoPo.getPkId());
                    });
            String taskId = saveForExcel(contrastListExcelDtos, contrastTask);
            resultMap.put("status", true);
            resultMap.put("taskId", taskId);
        } catch (Exception e) {
            resultMap.put("status", false);
            resultMap.put("message", e.getMessage());
        }
        return resultMap;
    }

    private String createSavePath() {
        String savePath = filePath + File.separator + "static/downExcel/contrastResult" + File.separator + cn.hutool.core.date.DateUtil.today() + File.separator + DateUtil.current(false);
        FileUtil.mkdir(savePath);
        return FilenameUtils.separatorsToSystem(savePath);
    }

    /*
     * @Method: modifyById <br>
     * @Param: [crossDbContrastListRequestDto] <br>
     * @Return: void <br>
     * @Description：根据主键更新<br>
     * @Author： wz.li<br>
     * @Date： 2023/7/24 19:42 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void modifyById(CrossDbContrastListRequestDto crossDbContrastListRequestDto) {
        int count = crossDbContrastListRepository.modifyById(converter.toCrossDbContrastListPo(crossDbContrastListRequestDto));
        if (count != 1) {
            throw new RuntimeException("更新跨库对比清单失败");
        }
    }

    /*
     * @Method: removeById <br>
     * @Param: [pkId] <br>
     * @Return: void <br>
     * @Description：根据主键删除<br>
     * @Author： wz.li<br>
     * @Date： 2023/7/24 19:42 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void removeById(String pkId) {
        int count = crossDbContrastListRepository.removeById(pkId);
        if (count != 1) {
            throw new RuntimeException("删除跨库对比清单失败");
        }
    }

    /*
     * @Method: batchRemoveById <br>
     * @Param: [pkIds] <br>
     * @Return: void <br>
     * @Description：跨库比对-批量删除<br>
     * @Author： wz.li<br>
     * @Date： 2023/7/24 19:41 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void batchRemoveById(List<String> pkIds) {
        for (String pkId : pkIds) {
            removeById(pkId);
        }
    }

    /*
     * @Method: queryList <br>
     * @Param: [crossDbContrastListRequestDto] <br>
     * @Return: java.util.List<com.hex.ds.hdtp.core.app.data.contrast.dto.response.CrossDbContrastListResponseDto> <br>
     * @Description：不分页查询<br>
     * @Author： wz.li<br>
     * @Date： 2023/7/25 16:58 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public List<CrossDbContrastListResponseDto> queryList(CrossDbContrastListRequestDto crossDbContrastListRequestDto) {
        List<CrossDbContrastListPo> crossDbContrastList =
                crossDbContrastListRepository.queryList(converter.toCrossDbContrastListPo(crossDbContrastListRequestDto));
        return converter.toCrossDbContrastListResponseDtoList(crossDbContrastList);
    }

    /**
     * @param crossDbContrastListRequestDto
     * @param pageDto
     * @Method queryListResultByPage
     * @Param
     * @Return com.hex.ds.hdtp.core.app.common.page.PageData
     * @Description 分页查询对比清单的结果
     * @Author Wang zhihao
     * @Date 2023/8/4 14:13
     * @Version V1.0
     */
    @Override
    public PageData queryListResultByPage(CrossDbContrastListRequestDto crossDbContrastListRequestDto, PageDto pageDto) {
        final Page<CrossDbContrastListPo> pageResult = crossDbContrastListRepository.queryListByPage(
                converter.toCrossDbContrastListPo(crossDbContrastListRequestDto), pageAndPageDtoConverter.toPage(pageDto));
        List<CrossDbContrastListResponseDto> responseDtos = converter.toCrossDbContrastListResponseDtoList(pageResult.getData());
        for (CrossDbContrastListResponseDto contrastListResponseDto : responseDtos) {
            List<CrossDbContrastResultResponseDto> contrastResults = crossDbContrastResultService.queryList(contrastListResponseDto.getPkId(), crossDbContrastListRequestDto.getRunLogPkId());
            Map<String, String> map = contrastResults.stream().collect(Collectors.toMap(CrossDbContrastResultResponseDto::getContrastType, CrossDbContrastResultResponseDto::getContrastStatus, (k, v) -> k));
            contrastListResponseDto.setContrastListResult(map);
            Map<String, String> messageMap = contrastResults.stream().filter(res -> res.getErrorMessage() != null).collect(Collectors.toMap(CrossDbContrastResultResponseDto::getContrastType, CrossDbContrastResultResponseDto::getErrorMessage, (k, v) -> k));
            contrastListResponseDto.setContrastListResultMessage(messageMap);
        }
        if (!CollUtil.isEmpty(responseDtos)) {
            responseDtos = getListResult(responseDtos, crossDbContrastListRequestDto);
        }
        return new PageData(responseDtos, pageResult.getTotal());
    }

    /**
     * @param pkId
     * @param crossDbContrastListRequestDtos
     * @Method addList
     * @Param
     * @Return void
     * @Description 新增跨库对比任务列表
     * @Author Wang zhihao
     * @Date 2023/8/6 21:53
     * @Version V1.0
     */
    @Override
    public void addList(String pkId, List<CrossDbContrastListRequestDto> crossDbContrastListRequestDtos) {
        for (CrossDbContrastListRequestDto contrastList : crossDbContrastListRequestDtos) {
            contrastList.setTaskPkId(pkId);
            add(contrastList);
        }
    }

    /**
     * @param crossDbContrastListRequestDtos
     * @Method batchModify
     * @Param
     * @Return void
     * @Description 批量更新比对任务列表
     * @Author Wang zhihao
     * @Date 2023/8/7 16:30
     * @Version V1.0
     */
    @Override
    public void batchModify(List<CrossDbContrastListRequestDto> crossDbContrastListRequestDtos) {
        for (CrossDbContrastListRequestDto contrastList : crossDbContrastListRequestDtos) {
            modifyById(contrastList);
        }
    }

    @Override
    public void motify(CrossDbContrastListRequestDto crossDbContrastListRequestDto) {
        modifyById(crossDbContrastListRequestDto);
    }

    @Override
    public void modifyStatus(String pkId, String status) {
        int count = crossDbContrastListRepository.modifyStatus(pkId, status);
        if (count != 1) {
            throw new RuntimeException("更新跨库对比清单状态失败");
        }
    }

    /**
     * @param taskPkId
     * @Method removeByTaskPkId
     * @Param
     * @Return void
     * @Description 根据任务主键删除
     * @Author Wang zhihao
     * @Date 2023/8/7 17:14
     * @Version V1.0
     */
    @Override
    public void removeByTaskPkId(String taskPkId) {
        crossDbContrastListRepository.removeByTaskPkId(taskPkId);
    }

    List<CrossDbContrastListResponseDto> getListResult(List<CrossDbContrastListResponseDto> responseDtos, CrossDbContrastListRequestDto crossDbContrastListRequestDto) {
        List<Predicate<CrossDbContrastListResponseDto>> filters = new ArrayList<>();

        addFilterIfNotEmpty(StrUtil.isEmpty(crossDbContrastListRequestDto.getIsCreateTable()) ? crossDbContrastListRequestDto.getIsCreateTable() : crossDbContrastListRequestDto.getIsCreateTable().toLowerCase(), "isCreateTable", filters, responseDtos.get(0));
        addFilterIfNotEmpty(StrUtil.isEmpty(crossDbContrastListRequestDto.getIsFieldCount()) ? crossDbContrastListRequestDto.getIsFieldCount() : crossDbContrastListRequestDto.getIsFieldCount().toLowerCase(), "isFieldCount", filters, responseDtos.get(0));
        addFilterIfNotEmpty(StrUtil.isEmpty(crossDbContrastListRequestDto.getIsHasData()) ? crossDbContrastListRequestDto.getIsHasData() : crossDbContrastListRequestDto.getIsHasData().toLowerCase(), "isHasData", filters, responseDtos.get(0));
        addFilterIfNotEmpty(StrUtil.isEmpty(crossDbContrastListRequestDto.getIsStatisticsDataCount()) ? crossDbContrastListRequestDto.getIsStatisticsDataCount() : crossDbContrastListRequestDto.getIsStatisticsDataCount().toLowerCase(), "isStatisticsDataCount", filters, responseDtos.get(0));
        addFilterIfNotEmpty(StrUtil.isEmpty(crossDbContrastListRequestDto.getIsAllField()) ? crossDbContrastListRequestDto.getIsAllField() : crossDbContrastListRequestDto.getIsAllField().toLowerCase(), "isAllField", filters, responseDtos.get(0));
        if (!filters.isEmpty()) {
            Predicate<CrossDbContrastListResponseDto> combinedFilter = filters.stream().reduce(Predicate::and).orElse(x -> true);
            responseDtos = responseDtos.stream().filter(combinedFilter).collect(Collectors.toList());
        }

        return responseDtos;
    }

    private void addFilterIfNotEmpty(String property, String propertyName, List<Predicate<CrossDbContrastListResponseDto>> filters, CrossDbContrastListResponseDto responseDtoOne) {

        if (!StrUtil.isEmpty(property) && responseDtoOne.getContrastListResult().keySet().contains(propertyName)) {
            filters.add(responseDto -> property.equals(responseDto.getContrastListResult().get(propertyName)));
        }
    }

    private String backupUploadFile(MultipartFile multipartFile) {
        String backUpPath = FilenameUtils.separatorsToSystem(filePath + File.separator + "static/uploadFile/ContrastListTemplate" + File.separator + DateUtil.today() + File.separator + DateUtil.current(false));
        FileUtil.mkdir(backUpPath);
        String updateFilePath = FilenameUtils.separatorsToSystem(backUpPath + File.separator);
        File file = upload(multipartFile, updateFilePath);
        log.debug("上传到服务器的EXCEL文件路径: {}", file.getPath());
        return file.getPath();
    }

    private List<ContrastListExcelDto> getExcelData(String backUpFilePath) {
        ExcelImportResult<ContrastListExcelDto> convertAfterList = readExcel(backUpFilePath);
        return convertAfterList.getList();
    }

    private ExcelImportResult<ContrastListExcelDto> readExcel(String backUpFilePath) {
        ImportParams importParams = new ImportParams();
        importParams.setStartRows(1);
        return ExcelImportUtil.importExcelMore(new File(backUpFilePath), ContrastListExcelDto.class, importParams);
    }

    @Transactional
    public String saveForExcel(List<ContrastListExcelDto> contrastListExcelDtos, CrossDbContrastTaskRequestDto contrastTask) {
        CrossDbContrastTaskPo crossDbContrastTaskPo = new CrossDbContrastTaskPo();
        BeanUtils.copyProperties(contrastListExcelDtos.get(0), crossDbContrastTaskPo);
        String taskId = StrUtil.isNotEmpty(contrastTask.getPkId()) ? contrastTask.getPkId() : crossDbContrastTaskRepository.addWithBackPkId(crossDbContrastTaskPo);

        contrastListExcelDtos.stream()
                .map(contrastListExcelDto -> {
                    CrossDbContrastListPo crossDbContrastListPo = new CrossDbContrastListPo();
                    BeanUtils.copyProperties(contrastListExcelDto, crossDbContrastListPo);
                    crossDbContrastListPo.setTaskPkId(taskId);
                    return crossDbContrastListPo;
                })
                .forEach(crossDbContrastListRepository::add);
        return taskId;
    }
}
